This section describes what the Image Compression Manager does that affects compressors. It then provides sample code that shows how the compressor components prepare for image compression and how to compress an entire image or a horizontal band of an image.
When compressing an image, the Image Compression Manager performs three major tasks:
Listing 1 shows how the Image Compression Manager calls the CDPreCompress function before an image is compressed. The compressor component returns information about how it is able to compress the image to the Image Compression Manager, so that it can fit the destination data to the requirements of the compressor component. This information includes compressor capabilities for
When your compressor component is called with the CDPreCompress function (described on CDPreCompress ), it can handle all aspects of the function itself, or only the most common ones. All image compressor components must handle at least one case.
Here is a list of some of the operations your compressor component can perform during compression. It describes parameters in the compression parameters structure (described on The Compression Parameters Structure ) and indicates the operations that are required and which flags in the compressor capabilities flags field of the compressor capabilities structure (described on The Compressor Capability Structure ) must be set to allow your compressor to handle them.
Listing 1 Preparing for simple compression operations
pascal long
CDPreCompress (Handle storage, register CodecCompressParams *p)
{
CodecCapabilities *capabilities = p->capabilities;
/*
First the compressor returns which depth input pixels it
supports based on what the application has available. This
compressor can only work with 32-bit input pixels.
*/
switch ( (*p->imageDescription)->depth ) {
case 16:
capabilities->wantedPixelSize = 32;
break;
default:
return(codecConditionErr);
break;
}
/*
If the buffer gets banded, return the smallest one the
compressor can handle.
*/
capabilities->bandMin = 2;
/*
If the buffer gets banded, return the increment
by which it should increase.
*/
capabilities->bandInc = 2;
capabilities->extendWidth = (*p->imageDescription)->width & 1;
capabilities->extendHeight = (*p->imageDescription)->height &
1;
/*
For efficiency, if the compressor could perform extension,
these flags would be set to 0.
*/
return(noErr);
}
Listing 2 shows how the Image Compression Manager calls the CDBandCompress function when it wants the compressor to compress a horizontal band of an image.
This example does not perform compression on bands with a bit depth of more than 1 or an extension of width and height. If the example did do so, it would handle these cases faster.
Listing 2 Performing simple compression on a horizontal band of an image
pascal long
CDBandCompress (Handle storage, register CodecCompressParams *p)
{
short width,height;
Ptr cDataPtr,dataStart;
short depth;
Rect sRect;
long offsetH,offsetV;
Globals **glob = (Globals **)storage;
register char *baseAddr;
long numLines,numStrips;
short rowBytes;
long stripBytes;
char mmuMode = 1;
register short y;
ImageDescription **desc = p->imageDescription;
OSErr result = noErr;
/*
If there is a progress function, give it an open call at
the start of this band.
*/
if (p->progressProcRecord.progressProc)
p->progressProcRecord.progressProc (codecProgressOpen, 0,
p->progressProcRecord.progressRefCon);
width = (*desc)->width;
height = (*desc)->height;
depth = (*desc)->depth;
dataStart = cDataPtr = p->data;
/*
Figure out offset to first pixel in baseAddr from the
pixel size and bounds.
*/
rowBytes = p->srcPixMap.rowBytes;
sRect = p->srcPixMap.bounds;
numLines = p->stopLine - p->startLine; /* number of scan
lines */
numStrips = (numLines+1)>>1; /* number of strips
in */
stripBytes = ((width+1)>>1) * 5;
/*
Adjust the source baseAddress to be at the beginning
of the desired rect.
*/
switch ( p->srcPixMap.pixelSize ) {
case 32:
offsetH = sRect.left<<2;
break;
case 16:
offsetH = sRect.left<<1;
break;
case 8:
offsetH = sRect.left;
break;
/*
This compressor does not handle the other cases directly.
*/
default:
result = codecErr;
goto bail;
}
offsetV = sRect.top * rowBytes;
baseAddr = p->srcPixMap.baseAddr + offsetH + offsetV;
/*
If there is not a data-unloading function,
adjust the pointer to the next band.
*/
if ( p->flushProcRecord.flushProc == nil ) {
cDataPtr += (p->startLine>>1) * stripBytes;
}
else { /*
Make sure the compressor can deal with the
data-unloading function in this case.
*/
if ( p->bufferSize < stripBytes ) {
result = codecSpoolErr;
goto bail;
}
}
/*
Perform the slower data-loading or progress operation, as
required.
*/
if ( p->flushProcRecord.flushProc ||
p->progressProcRecord.progressProc ) {
SharedGlobals *sg = (*glob)->sharedGlob;
for ( y=0; y < numStrips; y++) {
SwapMMUMode(&mmuMode);
CompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
SwapMMUMode(&mmuMode);
baseAddr += rowBytes<<1;
if ( p->flushProcRecord.flushProc ) {
if ( (result=
p->flushProcRecord.flushProc(cDataPtr,stripBytes,
p->flushProcRecord.flushRefCon)) != noErr) {
result = codecSpoolErr;
goto bail;
}
} else {
cDataPtr += stripBytes;
}
if (p->progressProcRecord.progressProc) {
if ( (result=
p->progressProcRecord.progressProc)
codecProgressUpdatePercent,
FixDiv(y,numStrips),
p->progressProcRecord.progressRefCon)
) != noErr ) {
result = codecAbortErr;
goto bail;
}
}
}
} else {
SharedGlobals *sg = (*glob)->sharedGlob;
short tRowBytes = rowBytes<<1;
SwapMMUMode(&mmuMode);
for ( y=numStrips; y--; ) {
CompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
cDataPtr += stripBytes;
baseAddr += tRowBytes;
}
SwapMMUMode(&mmuMode);
}
}